/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * reloc_elf.cc																*
 *																			*
 * Performs relocation on an ELF executable									*
 *																			*
\***************************************************************************/

// Includes
#include <system.h>
#include <stdio.h>
#include <reloc.h>
#include <reloc_elf.h>
#include <memory.h>
#include <keyboard.h>
#include <string.h>
#include <task.h>
#include <paging.h>
#include <pm.h>

// Class Definition

/// Relocates an ELF executable
bool ELFRelocation::RelocateExecutable()
{
	// elf file header
	Elf32_Ehdr* myHead = (Elf32_Ehdr*) GetBase();
	
	// get the base of the file
	uint_t myFileBase = GetBase();

	// disable multitasking so that nobody else can steal our space
	mtdisable();
	
	// get an address space to work with
	uint_t mypagedir = CreateAddressSpace( 1 );

	// whatever happens, the last directory given from CreateAddressSpace
	// is mapped 1:1
	uint_t mypagedir_virt = mypagedir;
	
	// check it
	if( !mypagedir ) { return false; };
	
	// object file, or executable?
	if( myHead->e_type == ET_EXEC )
	{
		// executable file (ie. multiple object files linked)
		
		// the program header table
		Elf32_Phdr* myProgramHeaders = (Elf32_Phdr*) (myHead->e_phoff + myFileBase);
		
		// iterate through each one
		int i,j;
		for( i = 0; i < myHead->e_phnum; i++ )
		{
			// is it loadable?
			if( myProgramHeaders[i].p_type == PT_LOAD )
			{
				// inform me
				kprintf( "phdr %d, vaddr = 0x%x, memsz = 0x%x, filesz = 0x%x\n", i, myProgramHeaders[i].p_vaddr, myProgramHeaders[i].p_memsz, myProgramHeaders[i].p_filesz );

				// the memory size of this section, in pages
				uint_t pagesize = ( myProgramHeaders[i].p_memsz / 4096 ) + 1;
				
				// we need enough space for this section - this also makes sure
				// everything is aligned to a page boundary and that our data
				// section doesn't interfere with the code
				uint_t physloc = (uint_t) pm_alloc( pagesize );
				
				// map all the addresses - map twice so that the caller can access the memory
				for( j = 0; j < pagesize; j++ )
				{
					// 1:1 mapping of the physical addresses
					MapUserPhysicalAddress( physloc + ( j * 0x1000 ), physloc + ( j * 0x1000 ), PF_PRESENT | PF_READWRITE | PF_USER );

					// virt:phys mapping for the actual executable
					MapPhysicalAddress( mypagedir_virt, myProgramHeaders[i].p_vaddr + ( j * 0x1000 ), physloc + ( j * 0x1000 ), PF_PRESENT | PF_READWRITE | PF_USER );
				}

				// copy the data across
				memcpy( (char*) (physloc), (char*) (myFileBase + myProgramHeaders[i].p_offset), myProgramHeaders[i].p_filesz );
				
				// clear out any gap space
				if( myProgramHeaders[i].p_filesz < myProgramHeaders[i].p_memsz )
					memset( (char*) (physloc + myProgramHeaders[i].p_filesz), 0, myProgramHeaders[i].p_memsz - myProgramHeaders[i].p_filesz );
			}
		}
		
		// get the end of the image
		uint_t endofimage = myProgramHeaders[myHead->e_phnum-1].p_vaddr + myProgramHeaders[myHead->e_phnum-1].p_memsz;
		
		// enable multitasking again, just before we create the process
		mtenable();

		// create the process
		CreateProcess( mypagedir, myHead->e_entry, endofimage, 2, fileName, 10 );
		
		// success!
		return true;
	}

	// enable multitasking again
	mtenable();
	
	// can't handle anything else
	return false;
}
